//: src/com/silvtar/sync/controller/SSFileTransporter.java
/* 
 * Created by 朴晟宁 <piaosn@gmail.com> on 4/21/2013. 
 * Copyright (c) 2013 朴晟宁. All rights reserved.
 * 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program;
 * 
 * *********************************************************************
 * 
 * Modify History:
 * Date        Version    Description
 * 4/22/2013   0.1        Create the class
 */

package com.silvtar.sync.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;

import com.silvtar.io.StdOut;
import com.silvtar.io.StreamTransporter;
import com.silvtar.util.Md5;

/**
 * 传输类 本类负责文件的传输
 * 
 * @author 朴晟宁/Silvtar
 * @author piaosn@gmail.com
 * @version 0.1
 */
public class SSFileTransporter {

    private LinkedList<SSTreeNode> outQueue      = null; // 传出队列

    private LinkedList<SSTreeNode> inQueue       = null; // 传入队列

    private String                 firRootPath_S = null; // 一号根目录

    private String                 secRootPath_S = null; // 二号根目录

    private String                 sourcePath_S  = null; // 传输时的源目录

    private String                 aimPath_S     = null; // 传输时的目标目录

    private int                    way           = 0;   // 传输方式

    private boolean                useMd5        = true; // 传输后是否使用MD5校验传输结果

    private InputStream            iStream       = null;

    private OutputStream           oStream       = null;

    // ---------------------------- 构造方法 ---------------------------
    /**
     * 传输器通过分析器获得源地址以及目的地址
     * 
     * @param analyzer
     *            分析器
     */
    public SSFileTransporter(SSAnalyzer analyzer) {

        outQueue = (LinkedList<SSTreeNode>) analyzer.getOutQueue().clone();
        inQueue = (LinkedList<SSTreeNode>) analyzer.getInQueue().clone();

        if (analyzer.isUseMd5()) {
            useMd5 = true;
        } else {
            useMd5 = false;
        }
    }

    /**
     * 设置必要的参数
     * 
     * @param firRoot
     *            源路径。本地地址格式：/USER/$HOME，网络地址格式：url://127.0.0.1。
     * @param secRoot
     *            目标路径。本地地址格式：/USER/$HOME，网络地址格式：url://127.0.0.1。
     * @param way
     *            传输方式。
     */
    public void setOption(String firRoot, String secRoot, int way) {

        this.firRootPath_S = firRoot;
        this.secRootPath_S = secRoot;
        this.way = way;
    }

    // ---------------------------- 主要函数 ---------------------------

    /**
     * 开始传输
     * 
     * @throws SSException
     * @throws IOException
     */
    public void startTransfer() throws SSException {

        if (firRootPath_S == null || secRootPath_S == null) {
            throw new SSException("没有设置同步目录");
        }

        // 网络地址类型，0表示为本地，1表示为网络
        int firAddrType = 0;
        int secAddrType = 0;

        if (firRootPath_S.regionMatches(0, "url://", 0, 6)) {
            firAddrType = 1;
        }
        if (secRootPath_S.regionMatches(0, "url://", 0, 6)) {
            secAddrType = 1;
        }

        // 如果source和aim都是本地地址
        if (0 == firAddrType && 0 == secAddrType) {
            // 从local传输到oppo
            sourcePath_S = firRootPath_S;
            aimPath_S = secRootPath_S;
            while (!outQueue.isEmpty()) {
                SSTreeNode currentTreeNode = outQueue.pop();
                copyFile(currentTreeNode);
            }

            // 从oppo传输到local
            sourcePath_S = secRootPath_S;
            aimPath_S = firRootPath_S;
            while (!inQueue.isEmpty()) {
                SSTreeNode currentTreeNode = inQueue.pop();
                copyFile(currentTreeNode);
            }
        }
        // 两者至少有一种是网络地址
        else {
            if (1 == firAddrType) {

            }

            if (1 == secAddrType) {

            }
        }
    }

    /**
     * 输出两条队列，与SSTree中的同名函数一致
     */
    public void print() {

        StdOut.println("OUT_QUEUE:  size = " + outQueue.size() + "\n");
        while (!outQueue.isEmpty()) {
            StdOut.println("  " + outQueue.pop().getName());
        }

        StdOut.println();
        StdOut.println("IN_QUEUE:  size = " + inQueue.size() + "\n");
        while (!inQueue.isEmpty()) {
            StdOut.println("  " + inQueue.pop().getName());
        }
    }

    // ---------------------------- 本地文件传送 ---------------------------
    /**
     * 本地拷贝文件，把文件从sourcePath_S传输到aimFile_S中。 可同时处理文件或文件夹。
     * 
     * @param currentFile_TN
     *            要传输的文件树节点
     */
    private void copyFile(SSTreeNode currentFile_TN) {

        String sourceFile_S = sourcePath_S + currentFile_TN.getRelativePath();
        String aimFile_S = aimPath_S + currentFile_TN.getRelativePath();

        File sourceFolder_F = new File(sourceFile_S);
        File aimFolder_F = new File(aimFile_S);

        if (!currentFile_TN.isDir()) {
            copyFile(sourceFile_S, aimFile_S);
            if (useMd5) {
                try {
                    if (!Md5.getFileMD5String(aimFolder_F).equals(
                            currentFile_TN.getMd5())) {
                        StdOut.printErrLog(this, sourceFile_S + " 与 "
                                + aimFile_S + " 校验不一致！");
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        } else {
            // 如果目录不存在，则新建。
            if (!aimFolder_F.exists()) {
                try {
                    aimFolder_F.mkdir();
                } catch (Exception e) {
                    StdOut.println("Create " + aimFolder_F.getName() + "Error!");
                }
            }

            // 获取所有子文件
            for (int i = 0; i < currentFile_TN.getChild().size(); i++) {
                copyFile(currentFile_TN.getChild().get(i));
            }
        }
    }

    /**
     * 本地拷贝文件,如果源文件是一个目录，则自动跳转到copyFolder函数。但是文件拷贝后的时间属性不一致。
     * 
     * @param sourceFile_S
     *            源文件路径
     * @param aimFile_S
     *            目标文件路径
     */
    private void copyFile(String sourceFile_S, String aimFile_S) {

        File iFile = new File(sourceFile_S);
        File oFile = new File(aimFile_S);

        if (!iFile.isFile()) {
            copyFolder(sourceFile_S, aimFile_S);
            StdOut.printWarningLog(this, sourceFile_S
                    + " 看起来是个文件夹，但是却被当做文件处理了。");
            return;
        }
        try {
            iStream = new FileInputStream(iFile);
            oStream = new FileOutputStream(oFile);
            StreamTransporter.transStream(iStream, oStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 本地拷贝文件夹。会拷贝目录下的所有文件，如果源目录是一个文件，则自动跳转到拷贝文件函数。可能会有bug存在。
     * 
     * @param sourceFolder_S
     *            源目录路径
     * @param aimFolder_S
     *            目标目录路径
     */
    private void copyFolder(String sourceFolder_S, String aimFolder_S) {

        File sourceFolder_F = new File(sourceFolder_S);
        File aimFolder_F = new File(aimFolder_S);

        // 检测这个目录是否为链接文件
        try {
            if (!sourceFolder_F.getCanonicalPath().equals(
                    sourceFolder_F.getAbsolutePath())
                    || sourceFolder_F.isFile()) {
                copyFile(sourceFolder_S, aimFolder_S);
                return;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 如果目录不存在，则新建。
        if (!aimFolder_F.exists()) {
            try {
                aimFolder_F.mkdir();
            } catch (Exception e) {
                StdOut.println("Create " + aimFolder_F.getName() + "Error!");
            }
        }

        // 获取所有子文件
        File[] filesInFolder = sourceFolder_F.listFiles();
        for (int i = 0; i < filesInFolder.length; i++) {
            String currentFileName = filesInFolder[i].getName();
            copyFile(sourceFolder_S + "/" + currentFileName, aimFolder_S + "/"
                    + currentFileName);
        }
    }
}
